/**
 * 
 */
package com.aaron.client;


import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.Button;

import com.google.gwt.user.client.HistoryListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;

import net.sf.jwc.gwt.user.client.ui.HtmlTextArea;
import org.gwtwidgets.client.ui.LightBox;

import java.util.*;


/**
 * @author aaron
 * 
 */



public class Editor extends Composite implements HistoryListener {
	/**
	 * Class for allowing integer values to be used in HashMap
	 * @author aaron
	 *
	 */
	private class Integer{
		public Integer(int i){
			this.i=i;
		}
		public int i;
	}

	/**
	 * Class for allowing commit message to be entered
	 */
	private class CommitPopup extends PopupPanel {
		private TextBox message_box;

		public CommitPopup() {
			super(true);
			FlexTable table = new FlexTable();
			table.setWidget(0, 0, new Label("Commit"));
			// table.getFlexCellFormatter().setColSpan(0, 0, 2);
			//name
			table.setWidget(1, 0, new Label("Message"));
			message_box = new TextBox();
			table.setWidget(1, 1, message_box);
			// add buttons
			table.setWidget(2, 1, new Button("Commit", new ClickListener() {
				public void onClick(Widget sender) {
					//check if any SyntaxColoredTextAreas are dirty
					if(anyDirtyEditors()){
						//display popup asking to save current changes
						//if yes, then perform saveall
						if(Window.confirm("Do you wish to save the current changes?")){
							(new SaveAllButtonCallback()).onClick(new Widget());
						}
					}
					SubversionServiceAsync svn_service = SubversionService.Util.getInstance();
					//commit the project
					svn_service.commit(project_name, message_box.getText(), auth, 
						new AsyncCallback() {
						public void onSuccess(Object result) {
							//TODO anything?

						}

						public void onFailure(Throwable caught) {
							// show reason why the new SVN project could not be commited
							Window.alert("Project could not be commited: "+caught.getMessage());
						}
					});
					hide();
				}
			}));
			// cancel
			table.setWidget(2, 2, new Button("Cancel", new ClickListener() {
				public void onClick(Widget sender) {
					hide();
				}
			}));

			setWidget(table);
			setStyleName("vivcode-popup");
		}
	}
	class SaveAsyncCallback implements AsyncCallback {
		private String path;
		public SaveAsyncCallback(String path){
			this.path=path;
		}
		public void onSuccess(Object result) {
			//remove dirty flag from current SyntaxColoredTextArea
			SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)textarea_map.get(path);
			textarea.setNotDirty();
			project_tree.setSelectedClean();
		}

		public void onFailure(Throwable caught) {
			// show reason why the new Document could not be saved
			Window.alert("Document could not be saved: "+caught.getMessage());
			saveall_button.setEnabled(true);
		}
	}
	class UpdateAsyncCallback implements AsyncCallback {
		public UpdateAsyncCallback(){
		}
		public void onSuccess(Object result) {
			//clear documents and SyntaxColoredTextAreas cache
			documents_map=new HashMap();
			textarea_map=new HashMap();
			id_map=new HashMap();
			project_tree.refresh();
		}

		public void onFailure(Throwable caught) {
			// show reason why the new Update could not be performed
			Window.alert("Update could not be performed: "+caught.getMessage());
		}
	}
	class SaveButtonCallback implements ClickListener {
		public void onClick(Widget sender) {
			//Window.alert("SaveButtonCallback.onClick()");
			String history_token=History.getToken();
			try{
				SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)textarea_map.get(history_token);
				if(textarea.isDirty()){
						Document dirty_document=textarea.getDocument();
						DocumentServiceAsync document_service = DocumentService.Util.getInstance();
						//String name, String svn_url, String svn_username, String svn_password, String auth, AsyncCallback callback
						document_service.mergeDocument(dirty_document, auth, new SaveAsyncCallback(dirty_document.getPath()));
				}
				else{
					Document dirty_document=textarea.getDocument();
					Window.alert("Not sending "+dirty_document.getPath()+" to server.");
				}
			}
			catch(ClassCastException e){
				Window.alert(e.getLocalizedMessage());
			}
			save_button.setEnabled(false);
		}
	}
	class SaveAllButtonCallback implements ClickListener {
		public void onClick(Widget sender) {
			Window.alert("SaveAllButtonCallback.onClick()");
			Collection textareas=textarea_map.values();
			Iterator itr=textareas.iterator();
			//loop through all SyntaxColoredTextAreas
			while(itr.hasNext()){
				try{
					//if dirty, then send the document to the server for saving
					SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)itr.next();
					//loop through all;
					if(textarea.isDirty()){
						Document dirty_document=textarea.getDocument();
						DocumentServiceAsync document_service = DocumentService.Util
						.getInstance();
						document_service.setDocument(dirty_document, auth, new SaveAsyncCallback(dirty_document.getPath()));
					}
				}
				catch(ClassCastException e){
					Window.alert(e.getLocalizedMessage());
				}
			}
			saveall_button.setEnabled(false);
		}
	}
	class UpdateButtonCallback implements ClickListener {
		public void onClick(Widget sender) {
			//fetch update from svn,
			SubversionServiceAsync subversion_service = SubversionService.Util.getInstance();
			subversion_service.update(project_name, auth, new UpdateAsyncCallback());
		}
	}
	private String project_name;
	private String auth;
	private FlowPanel center_panel;
	private Widget center_widget;
	//String(path):Document
	private HashMap documents_map;
	
	//String(path):SyntaxColoredTextArea
	private HashMap textarea_map;
	
	//String(path):Integer
	private HashMap id_map;
	private int max_id;
	private int current_id;
	
	Button commit_button;
	Button update_button;
	Button saveall_button;
	Button save_button;
	
	ProjectTree project_tree;

	LightBox commit_dialog;

	public Editor(String project_name, String auth) {
		this.project_name=project_name;
		this.auth=auth;
		documents_map=new HashMap();
		textarea_map=new HashMap();
		id_map=new HashMap();
		max_id=-1;
		current_id=-1;
		center_panel=new FlowPanel();

		DockPanel dock = new DockPanel();
		Label label = new Label(project_name);
		FlowPanel top = new FlowPanel();
		project_tree = new ProjectTree(project_name, auth);
		

		//TODO add to center some kind of project statistics page?

		center_panel.addStyleName("vivcode-editor-center-panel");


		top.add(label);

		//add save, save all, update, commit buttons in reverse order
		//buttons will float right to restore correct order
		commit_dialog = new LightBox(new CommitPopup());
		commit_button=new Button("Commit");
		commit_button.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				// display add project dialog
				commit_dialog.show();
				commit_button.setEnabled(false);
			}
		});
		commit_button.addStyleName("float-right");
		top.add(commit_button);

		update_button=new Button("Update");
		update_button.addClickListener(new UpdateButtonCallback());
		update_button.addStyleName("float-right");
		top.add(update_button);

		saveall_button=new Button("Save All");
		saveall_button.addClickListener(new SaveAllButtonCallback());
		saveall_button.addStyleName("float-right");
		saveall_button.setEnabled(false);
		top.add(saveall_button);

		save_button=new Button("Save");
		save_button.addClickListener(new SaveButtonCallback());
		save_button.addStyleName("float-right");
		save_button.setEnabled(false);
		top.add(save_button);

		project_tree.addStyleName("vivcode-project-tree");
		
		Label footer=new Label("Powered by TinyMCE");
		footer.addStyleName("footer");

		dock.add(top, DockPanel.NORTH);
		dock.add(project_tree, DockPanel.WEST);
		dock.add(center_panel, DockPanel.CENTER);
		dock.add(footer, DockPanel.SOUTH);
		dock.setWidth("100%");
		dock.setHeight("100%");
		RootPanel.get().add(new LinksPanel(auth));
		RootPanel.get().add(dock);

		//add the history listener
		History.addHistoryListener(this);

		//link the TinyMCE onchange_callback to a real java method: onChangeCallback
		linkOnChangeCallback();

		//initialize for Composite
		initWidget(dock);
		this.addStyleName("vivcode-editor");
	}
	/**
	 * @param historyToken - the path of the changed-to document
	 */
	public void onHistoryChanged(String historyToken) {
		//Window.alert("History Changed");
		//check if document is cached
		if(id_map.containsKey(historyToken)){
			Element element;
//			hide the current widget
			if(current_id>-1){
				element=DOM.getElementById("mce_editor_"+(current_id)+"_parent");
				if(element!=null){
					DOM.setStyleAttribute(element, "display", "none");
				}
			}
			//show the cached widget
			Integer id=(Integer)id_map.get(historyToken);
			current_id=id.i;
			element=DOM.getElementById("mce_editor_"+(current_id)+"_parent");
			DOM.setStyleAttribute(element, "display", "inline");
			//Window.alert(document.getText());
			
			//change the save button as is appropriate
			SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)textarea_map.get(historyToken);
			save_button.setEnabled(textarea.isDirty());
		}
		else{
			DocumentServiceAsync document_service=DocumentService.Util.getInstance();
			document_service.getDocument(historyToken, auth, new AsyncCallback(){
				public void onSuccess(Object result) {
					Document document=(Document)result;
					//cache the document for later use
					documents_map.put(document.getPath(), document);
					//create a new source view from the document
					SyntaxColoredTextArea textarea=new SyntaxColoredTextArea(document);
					//cache the widget for later use
					textarea_map.put(document.getPath(), textarea);
					//hide the current widget
					if(current_id>-1){
						Element element=DOM.getElementById("mce_editor_"+(current_id)+"_parent");
						if(element!=null){
							DOM.setStyleAttribute(element, "display", "none");
						}
					}
					max_id++;
					current_id=max_id;
					center_widget = textarea;

					//cache the id for later use
					id_map.put(document.getPath(), new Integer(current_id));
					//add the source view to the center panel
					center_panel.add(center_widget);
//					change the save button as is appropriate
					save_button.setEnabled(false);
				}

				public void onFailure(Throwable caught) {
					Exception e=(Exception)caught;
					center_panel.add(new Label("Could not load file: "+e.getLocalizedMessage()));
					Window.alert("Could not load file: "+e.getLocalizedMessage());
				}
			});
		}

	}
	public  void onChangeCallback(){
		//perform action when syntaxcoloredtextarea is changed
		//Use current history token to access current textarea/document 
		String history_token=History.getToken();
		SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)textarea_map.get(history_token);
		//sync the textarea with the document
		textarea.syncToDocument();
		textarea.updateSyntaxHighlighting();
		
		//TinyMCE editor changed, enable save, saveall, commit buttons
		save_button.setEnabled(true);
		saveall_button.setEnabled(true);
		commit_button.setEnabled(true);
		project_tree.setSelectedDirty();
	}
	private boolean anyDirtyEditors(){
		Object[] textareas=textarea_map.entrySet().toArray();
		//get current SyntaxColoredTextArea
		for(int i=0; i<textareas.length; i++){
			//if dirty, then send the document to the server for saving
			SyntaxColoredTextArea textarea=(SyntaxColoredTextArea)textareas[i];
			if(textarea.isDirty()){
				return true;
			}
		}
		return false;
	}
	private native void linkOnChangeCallback()/*-{
		var editor=this;
		  $wnd.onChangeCallback=function(){
		  	editor.@com.aaron.client.Editor::onChangeCallback()();
		  }
		  if(!$wnd.onChangeCallback){
		  	alert("onChangeCallback not linked");
		  }
	}-*/;
}
